package hsl.study.videocapturesystem.service.impl;

import hsl.study.videocapturesystem.config.VideoCaptureProperties;
import hsl.study.videocapturesystem.service.MultiModelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.content.Media;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.ollama.api.OllamaModel;
import org.springframework.ai.ollama.api.OllamaOptions;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.stereotype.Service;
import org.springframework.util.MimeTypeUtils;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class MultiModelServiceImpl implements MultiModelService {


    @Resource
    private OllamaChatModel ollamaChatModel;

    @Resource
    private VideoCaptureProperties videoCaptureProperties;

    @Resource
    private ThreadPoolExecutor threadPoolExecutor;

    // 新增：事件发布器，用于将AI结果通知Controller
    @Resource
    private ApplicationEventPublisher eventPublisher;

    private BlockingQueue<CapturedImage> imageQueue;
    private String videoUrl;
    private int frequency;
    private String aiResultFile;
    private Process ffmpegProcess;
    private Thread captureThread;
    private BufferedWriter resultWriter;
    // 添加服务状态标志
    private volatile boolean isRunning = false;

    /**
     * 启动视频捕获和AI处理
     */
    @Override
    public void startProcessing() {
        if (isRunning) {
            log.warn("服务已在运行中");
            return;
        }

        log.info("启动视频捕获和AI处理服务");

        init();

        isRunning = true;
    }

    /**
     * 停止视频捕获和AI处理
     */
    @Override
    public void stopProcessing() {
        if (!isRunning) {
            log.warn("服务未运行");
            return;
        }

        log.info("停止视频捕获和AI处理服务");

        shutdown();

        isRunning = false;
    }

    public void init() {
        // 从配置加载参数
        this.videoUrl = videoCaptureProperties.getVideoUrl();
        this.frequency = videoCaptureProperties.getFrequency();
        this.aiResultFile = videoCaptureProperties.getAiResultFile();

        // 初始化有界队列（容量建议20-30）
        int queueCapacity = videoCaptureProperties.getQueueCapacity();
        this.imageQueue = new LinkedBlockingQueue<>(queueCapacity);

        // 初始化结果文件写入器
        initResultWriter();

        // 启动帧捕获线程
        this.captureThread = new Thread(this::captureFrames, "Frame-Capturer");
        captureThread.start();

        // 启动AI处理线程
        threadPoolExecutor.execute(this::processImagesWithAI);
    }

    /**
     * 服务关闭方法
     * 确保资源被正确释放
     */
    @PreDestroy
    public void shutdown() {

        // 1. 先优雅关闭FFmpeg（发送中断信号，等待其结束）
        if (ffmpegProcess != null && ffmpegProcess.isAlive()) {
            ffmpegProcess.destroy(); // 发送正常终止信号（而非强制销毁）
            try {
                // 等待FFmpeg完成当前帧并退出（最多等待5秒）
                if (ffmpegProcess.waitFor(5, TimeUnit.SECONDS)) {
                } else {
                    // 若5秒后仍未退出，再强制销毁（作为兜底）
                    ffmpegProcess.destroyForcibly();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        // 终止捕获线程
        if (captureThread != null && captureThread.isAlive()) {
            captureThread.interrupt();
        }

        // 关闭线程池
        threadPoolExecutor.shutdown();
        try {
            if (!threadPoolExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
                threadPoolExecutor.shutdownNow();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }


        // 关闭结果文件写入器
        closeResultWriter();
    }

    /**
     * 初始化结果文件写入器
     * 创建目录和文件（如果不存在）
     */
    private void initResultWriter() {
        try {
            Path resultPath = Paths.get(aiResultFile);
            if (resultPath.getParent() != null && !Files.exists(resultPath.getParent())) {
                Files.createDirectories(resultPath.getParent());
            }
            resultWriter = Files.newBufferedWriter(
                    resultPath,
                    StandardCharsets.UTF_8,
                    StandardOpenOption.CREATE,
                    StandardOpenOption.APPEND
            );
            log.info("AI结果文件初始化完成: {}", aiResultFile);
        } catch (IOException e) {
            log.error("AI结果文件初始化失败", e);
            throw new RuntimeException("AI结果文件初始化失败", e);
        }
    }

    /**
     * 关闭结果文件写入器
     */
    private void closeResultWriter() {
        if (resultWriter != null) {
            try {
                resultWriter.close();
            } catch (IOException e) {
                log.error("关闭结果文件写入器失败", e);
            }
        }
    }

    // 图像处理状态机
    private enum ParserState {WAITING, IN_IMAGE}

    /**
     * 帧捕获核心逻辑
     * 使用FFmpeg捕获RTSP流并解析JPEG帧
     */
    private void captureFrames() {
        int maxRetries = 3;
        int retryCount = 0;
        final long RETRY_INTERVAL = 5000;

        while (retryCount < maxRetries && !Thread.currentThread().isInterrupted()) {
            try {
                log.info("启动FFmpeg进程...");
                // 构建FFmpeg命令：从视频流按指定帧率截取图像，通过管道输出JPEG
                ProcessBuilder pb = new ProcessBuilder(
                        "ffmpeg",  // FFmpeg可执行文件（需确保系统环境变量中存在ffmpeg）
                        "-hide_banner",  // 隐藏FFmpeg启动横幅信息
                        "-loglevel", "error",  // 日志级别：只输出错误信息
                        "-fflags", "+genpts+discardcorrupt",  // 允许生成时间戳，丢弃损坏的数据包
                        "-flags", "+low_delay",               // 低延迟模式
                        "-probesize", "32",                   // 减小探测大小，加快启动
                        "-analyzeduration", "0",              // 不分析时长，适用于实时流
                        "-rtsp_transport", "tcp",             // 使用TCP而非UDP（减少丢包）
                        "-i", videoUrl,  // 输入源：视频地址（如rtsp://xxx或本地文件）
                        "-r", String.valueOf(frequency),  // 帧率：截取频率 frequency
                        "-f", "image2pipe",  // 输出格式：图像管道（通过标准输出流传递图像数据）
                        "-vcodec", "mjpeg",  // 编码格式：JPEG（确保输出为JPG图像）
                        "-q:v", "15",         // JPEG质量参数：1-31，1为最高质量
                        "-"  // 输出目标：标准输出流（通过管道传递给当前程序）
                );
                pb.redirectErrorStream(false);
                ffmpegProcess = pb.start();

                // 处理FFmpeg错误流
                new Thread(() -> {
                    try (BufferedReader errorReader = new BufferedReader(
                            new InputStreamReader(ffmpegProcess.getErrorStream()))) {
                        String errorLine;
                        while ((errorLine = errorReader.readLine()) != null) {
                            log.error("FFmpeg错误: {}", errorLine);
                        }
                    } catch (IOException e) {
                        log.error("FFmpeg错误流读取失败", e);
                    }
                }, "FFmpeg-Error-Handler").start();

                // JPEG标记
                byte[] jpegStart = {(byte) 0xFF, (byte) 0xD8};
                byte[] jpegEnd = {(byte) 0xFF, (byte) 0xD9};

                // 解析状态
                ParserState state = ParserState.WAITING;
                ByteArrayOutputStream imageBuffer = new ByteArrayOutputStream();
                int frameCount = 0;

                try (InputStream in = ffmpegProcess.getInputStream()) {
                    byte[] buffer = new byte[8192];
                    int bytesRead;
                    int searchStart = 0;

                    while (!Thread.currentThread().isInterrupted() &&
                            (bytesRead = in.read(buffer)) != -1) {

                        for (int i = 0; i < bytesRead; i++) {
                            switch (state) {
                                case WAITING:
                                    // 检查起始标记
                                    if (i + 1 < bytesRead &&
                                            buffer[i] == jpegStart[0] &&
                                            buffer[i + 1] == jpegStart[1]) {
                                        imageBuffer.reset();
                                        imageBuffer.write(jpegStart);
                                        state = ParserState.IN_IMAGE;
                                        i++; // 跳过第二个字节
                                        searchStart = i + 1;
                                    }
                                    break;

                                case IN_IMAGE:
                                    // 检查结束标记
                                    if (i + 1 < bytesRead &&
                                            buffer[i] == jpegEnd[0] &&
                                            buffer[i + 1] == jpegEnd[1]) {

                                        imageBuffer.write(jpegEnd);
                                        byte[] imageData = imageBuffer.toByteArray();
                                        frameCount++;

                                        // 生成唯一图片名
                                        String imageName = Instant.now().toEpochMilli() + ".jpg";

                                        // 队列操作（线程安全）
                                        synchronized (imageQueue) {
                                            // 队列满时移除最旧元素
                                            if (imageQueue.remainingCapacity() == 0) {
                                                imageQueue.poll();
                                            }
                                            // 添加新元素
                                            imageQueue.put(new CapturedImage(imageName, imageData));
                                        }

                                        // 重置状态
                                        imageBuffer.reset();
                                        state = ParserState.WAITING;
                                        i++; // 跳过结束标记第二个字节
                                    } else {
                                        // 写入当前字节
                                        imageBuffer.write(buffer[i]);
                                    }
                                    break;
                            }
                        }
                    }
                    log.info("捕获帧数: {}", frameCount);
                }

                int exitCode = ffmpegProcess.waitFor();
                log.info("FFmpeg退出码: {}", exitCode);
                return; // 成功执行后退出重试循环

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.info("捕获线程被中断");
                break;
            } catch (Exception e) {
                log.error("捕获异常", e);
            } finally {
                if (ffmpegProcess != null && ffmpegProcess.isAlive()) {
                    ffmpegProcess.destroyForcibly();
                }

                // 重试逻辑
                retryCount++;
                if (retryCount < maxRetries) {
                    log.warn("{}秒后重试...", RETRY_INTERVAL / 1000);
                    try {
                        Thread.sleep(RETRY_INTERVAL);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }

        if (retryCount >= maxRetries) {
            log.error("达到最大重试次数，停止捕获");
        }
    }

    /**
     * AI处理线程
     * 从队列获取图像并进行AI处理
     */
    private void processImagesWithAI() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                CapturedImage image = imageQueue.take();
                log.info("处理图像: {}", image.getName());

                // 提交AI任务
                threadPoolExecutor.submit(() -> {
                    // 生成AI结果
                    String aiResult = generateTextFromImage("介绍一下图片中的内容并用中文输出", image);

                    // 写入结果文件
                    writeAiResultToFile(image.getName(), aiResult);

                    // 创建结果对象返回给Controller
                    Map<String, Object> resultData = new HashMap<>();
                    resultData.put("imageName", image.getName());
                    resultData.put("aiResult", aiResult);
                    resultData.put("timestamp", System.currentTimeMillis());

                    // 发布事件通知Controller（新增）
                    eventPublisher.publishEvent(new AIResultEvent(this, resultData));

                    // 此处可添加前端推送逻辑（示例）
                    // pushToFrontend(image.getName(), aiResult);
                });

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.info("AI处理线程被中断");
            }
        }
    }

    /**
     * 生成图像描述
     *
     * @param question 提示问题
     * @param image    图像对象
     * @return AI生成的文本描述
     */
    private String generateTextFromImage(String question, CapturedImage image) {
        try {
            ByteArrayResource resource = new ByteArrayResource(image.getData());
            UserMessage userMessage = UserMessage.builder()
                    .text(question)
                    .media(new Media(MimeTypeUtils.IMAGE_JPEG, resource))
                    .build();

            Prompt prompt = new Prompt(userMessage,
                    OllamaOptions.builder().model(OllamaModel.LLAVA).build());

            ChatResponse response = ollamaChatModel.call(prompt);
            String text = response.getResult().getOutput().getText();
            System.out.println("text = " + text);
            return text;
        } catch (Exception e) {
            log.error("AI处理失败", e);
            return "处理失败: " + e.getMessage();
        }
    }

    /**
     * 写入结果文件
     *
     * @param imageName 图片名称
     * @param aiResult  AI处理结果
     */
    private void writeAiResultToFile(String imageName, String aiResult) {
        try {
            String cleanResult = aiResult.replace("\n", " ").replace("\r", "");
            String outputLine = imageName + " == " + cleanResult + System.lineSeparator();

            synchronized (this) {
                resultWriter.write(outputLine);
                resultWriter.flush(); // 确保及时写入
            }
        } catch (IOException e) {
            log.error("写入结果失败", e);
        }
    }

    /**
     * 图像封装类
     */
    private static class CapturedImage {
        private final String name;
        private final byte[] data;

        public CapturedImage(String name, byte[] data) {
            this.name = name;
            this.data = data;
        }

        public String getName() {
            return name;
        }

        public byte[] getData() {
            return data;
        }
    }

    /**
     * AI结果事件类（新增）
     * 用于将AI处理结果传递给Controller
     */
    public static class AIResultEvent {
        private final Object source;
        private final Map<String, Object> resultData;

        public AIResultEvent(Object source, Map<String, Object> resultData) {
            this.source = source;
            this.resultData = resultData;
        }

        public Map<String, Object> getResultData() {
            return resultData;
        }
    }
}